package neurons;

import java.util.HashSet;
import java.util.Set;

import signal.SignalFactory;
import signal.SignalFactoryInterface;
import synapses.FromExteriorSynapse;
import synapses.NormalSynapseFromNeuron;
import synapses.NormalSynapseToNeuron;
import synapses.SynapseInterface;
import synapses.TowardsExteriorSynapse;

public class NeuronNetFactory implements NeuronNetFactoryInterface {
	@Override
	public NeuronNet createMonoLayer(int nbInSynapses, int nbNeurons) {
		Set<NeuronInterface> inNeurons = new HashSet<NeuronInterface>();
		for (int i = 0; i < nbNeurons; i++) {
			InternalFunctionInterface functionHeavySide = new InternalFunctionHeavySide();
			SignalFactoryInterface signalFactory = new SignalFactory();
			NeuronInterface neuron = new NeuronHeavyside(functionHeavySide,
					signalFactory);
			for (int j = 0; j < nbInSynapses; j++) {
				new FromExteriorSynapse(neuron);

			}
			for (int j = 0; j < nbNeurons; j++) {
				new TowardsExteriorSynapse(neuron);
			}
			inNeurons.add(neuron);
		}
		NeuronLayer inLayer = new NeuronLayer(inNeurons);
		return new NeuronNet(inLayer, inLayer, inLayer);
	}

	@Override
	public NeuronNet createMultiLayer(int nbInSynapses, int nbInNeurons,
			int nbOutNeurons, int... nbHiddenNeurons)
			throws TooFewHiddenLayersException {

		Set<NeuronInterface> inNeurons = new HashSet<NeuronInterface>();
		for (int i = 0; i < nbInNeurons; i++) {
			InternalFunctionInterface functionHeavySide = new InternalFunctionHeavySide();
			SignalFactoryInterface signalFactory = new SignalFactory();
			NeuronInterface neuron = new NeuronHeavyside(functionHeavySide,
					signalFactory);
			for (int j = 0; j < nbInSynapses; j++) {
				new FromExteriorSynapse(neuron);
			}
			inNeurons.add(neuron);
		}
		NeuronLayer inLlayer = new NeuronLayer(inNeurons);

		NeuronLayer hiddenLayer = null;
		if (nbHiddenNeurons.length < 1) {
			throw new TooFewHiddenLayersException();
		} else {
			Set<NeuronInterface> hiddenNeurons = new HashSet<NeuronInterface>();
			for (int nbOfNeuronsForThatLayer : nbHiddenNeurons) {
				for (int i = 0; i < nbOfNeuronsForThatLayer; i++) {
					InternalFunctionInterface functionId = new InternalFunctionIdentity();
					SignalFactoryInterface signalFactory = new SignalFactory();
					NeuronInterface neuron = new NeuronHeavyside(functionId,
							signalFactory);
					hiddenNeurons.add(neuron);
				}
			}
			hiddenLayer = new NeuronLayer(hiddenNeurons);
		}

		Set<NeuronInterface> outNeurons = new HashSet<NeuronInterface>();
		for (int i = 0; i < nbOutNeurons; i++) {
			InternalFunctionInterface functionId = new InternalFunctionIdentity();
			SignalFactoryInterface signalFactory = new SignalFactory();
			NeuronInterface neuron = new NeuronId(functionId, signalFactory);
			for (int j = 0; j < nbOutNeurons; j++) {
				new TowardsExteriorSynapse(neuron);
			}
			outNeurons.add(neuron);

		}
		NeuronLayer outLayer = new NeuronLayer(outNeurons);

		for (NeuronInterface neuronIn : inNeurons) {
			for (NeuronInterface neuronOut : outNeurons) {
				connect(neuronIn, neuronOut);
			}
		}

		return new NeuronNet(inLlayer, hiddenLayer, outLayer);
	}

	private void connect(NeuronInterface neuronIn, NeuronInterface neuronOut) {
		NormalSynapseFromNeuron startSynapse = new NormalSynapseFromNeuron(
				neuronIn);
		NormalSynapseToNeuron endSynapse = new NormalSynapseToNeuron(neuronOut);
		// inSynapse.setSynapseIn(outSynapse);
		startSynapse.setSynapseOut(endSynapse);
	}

}
